Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve nullability annotations and generic variance #576

Merged
merged 5 commits into from
Jun 10, 2024

Conversation

vlsi
Copy link
Contributor

@vlsi vlsi commented Jun 6, 2024

This is a draft, however, it should more-or-less work.

Notable changes:

  • @NullMarked package annotation does not automatically inherit to sub-packages, so @NullMarked must be duplicated manually (I've added many package-info.java files)
  • By default, Kotlin assumes that type parameter is nullable. For instance <T> fun(value: T) {... means that it is valid to use String? for T. At the same time, Java defaults to non-null, so if nullable T should be allowed, then the declaration should be <T extends @Nullable Object>
  • In 99.9% of the cases @NotNull is not needed. If IDEA suggest "overriding method does ..." and suggests adding @NotNull, then most likely @NullMarked package-info.java is missing
  • I updated Consumer<T> -> Consumer<? super T>; Function<ARG, RESULT> -> Function<? super ARG, ? extends RESULT> and so on. Unfortuantely, with Java one has to duplicate those super/extends all over the place
  • In most cases List<? extends T> is effectively a ReadOnlyList<T>. It is helpful in both generic variance (accepting more matching arguments) and it helps to understand the method signature as in "ok, the method accepts List<? extends T>, so it does not modify the list"

@lcahmu
Copy link

lcahmu commented Jun 6, 2024

Thanks @vlsi, very much appreciated! I tried using this branch in my project and get a Checker error using Arbitrary.just(..). Looks like that one got overlooked?

@vlsi
Copy link
Contributor Author

vlsi commented Jun 6, 2024

What is the error?

@lcahmu
Copy link

lcahmu commented Jun 6, 2024

It's a type mismatch. It's still <@Nullable T> Arbitrary<T> just(@Nullable T) where nearby methods were updated to <T extends @Nullable Object>. ArbitrariesFacade.just(..) was updated that way, so looks like it just got missed on Arbitrary.

@jlink
Copy link
Collaborator

jlink commented Jun 7, 2024

@vlsi What's missing in your opinion to make it a mergeable, non-WIP PR?

@@ -454,7 +454,7 @@ public static CharacterArbitrary chars() {
* @return a new arbitrary instance
*/
@API(status = MAINTAINED, since = "1.1.1")
public static <T> Arbitrary<T> create(Supplier<T> supplier) {
Copy link
Collaborator

@jlink jlink Jun 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that methods that previously returned <T> should now return <T extends @Nullable...>. It might be the right choice, but then it should be a different PR IMO. Or is it necessary to peacify the compiler?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean you want forbid creating Arbitrary<String?> with this method?

Consider the use case: Arbitraries.create(() -> random.nextBoolean() ? "hi" : null).
Previous declaration (<T>) forbids such use cases, however, they are pretty valid.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm with you that the new signature is correct, but it changes the previous return type. So I'd rather have it in a different PR. If it's too bothersome to pick those changes out, leave them in.

@@ -217,7 +217,7 @@ public BuilderCombinator<B> in(BiFunction<B, @Nullable T, B> toFunction) {
* @param setter Use value provided by arbitrary to change a builder's property.
* @return new {@linkplain BuilderCombinator} instance with same embedded builder
*/
public BuilderCombinator<B> inSetter(BiConsumer<B, T> setter) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same argument here. If correct (have to think it through), then a different PR.

@@ -79,6 +79,6 @@ default StreamableArbitrary<T, U> ofSize(int size) {
* @return new arbitrary instance
*/
@API(status = MAINTAINED, since = "1.7.3")
StreamableArbitrary<@Nullable T, U> uniqueElements(Function<@Nullable T, Object> by);
StreamableArbitrary<T, U> uniqueElements(Function<? super T, ?> by);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change to the second type parameter should then also entail a change in @UniqueElements annotation and the classes that process it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class<? extends Function<?, ?>> by() would allow specificly typed implementations. But it can also be done in a follow-up PR.

@vlsi vlsi changed the title WIP: fixup nullability annotations and generic variance Improve nullability annotations and generic variance Jun 7, 2024
@vlsi
Copy link
Contributor Author

vlsi commented Jun 7, 2024

I think it should be good to go assuming the tests pass.
I understand the PR somehow includes both nullability and generic variance changes, however often it is the case @Nullable is at the same line as ? extends/super. It would take me significant effort to split the changes.

Note: there might be many more generic variance improvements to come, and I touched only the signatures that were close to the nullability fixes.

I'm fine answering questions and resolving the outstanding issues, however I will not spend my time on splitting the changes into different PRs.

@jlink
Copy link
Collaborator

jlink commented Jun 10, 2024

Many thanks @vlsi for the work and the focus on so many details!

It also show how tedious it can become to implement null safety and variance constraints in a language that has not be designed for that. I'm hesitant if I'll aim for that in jqwik 2, too.

@jlink jlink merged commit b9d47a1 into jqwik-team:main Jun 10, 2024
6 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants